package com.broada.apm.logic.impl;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.broada.apm.base.BaseLogic;
import com.broada.apm.cache.AreaManager;
import com.broada.apm.cache.BrowserManager;
import com.broada.apm.cache.DeviceManager;
import com.broada.apm.cache.OSManager;
import com.broada.apm.entity.AreaPerformance;
import com.broada.apm.entity.ErrorCodeCount;
import com.broada.apm.entity.OrderClass;
import com.broada.apm.entity.PerformanceDetail;
import com.broada.apm.entity.PerformanceGroup;
import com.broada.apm.entity.Service;
import com.broada.apm.indicator.api.common.ApmDataIndicatorException;
import com.broada.apm.indicator.api.model.SearchCondition;
import com.broada.apm.indicator.api.model.SearchConditionComparator;
import com.broada.apm.indicator.api.util.AggregationUtil;
import com.broada.apm.logic.ApplicationLogic;
import com.broada.apm.logic.PerformanceLogic;
import com.broada.apm.logic.ServerLogic;
import com.broada.apm.model.ApplicationSystem;
import com.broada.apm.model.BaseResource.RESOURCE_TYPE;
import com.broada.apm.model.Dimension.Area;
import com.broada.apm.model.PagingList;
import com.broada.apm.model.PerformanceView;
import com.broada.apm.model.Resource;
import com.broada.apm.utils.ExceptionUtils;
import com.broada.apm.utils.IPUtils;
import com.broada.apm.utils.JsonUtil;
import com.google.common.collect.Lists;

/*
 * 2015-4-15 jialin bugfix: 重写了queryPerformanceByTrade() 和 queryProtocalPerformance()中补充URL信息越界错误 2015-4-15 jialin 重写了queryPerformanceByTrade() 和
 * queryProtocalPerformance()
 */
public class PerformanceLogicImpl extends BaseLogic implements PerformanceLogic {

	private Logger log = LoggerFactory.getLogger(PerformanceLogicImpl.class);
	private static double APP_AVAILABLE_LOWWER = 60;
	private static double APP_AVAILABLE_UPPER = 100;
	@Autowired
	private ApplicationLogic appLogic;
	@Autowired
	private ServerLogic serverLogic;

	/**
	 * 获取节点和系统的性能信息
	 * 
	 * @param size 粒度
	 */
	@Override
	public List<PerformanceGroup> queryPerformance(String[] appIds, String[] agentIds, long startDate, long endDate,
			String size) {
		if (StringUtils.isBlank(size)) {
			return queryPerfById(appIds, agentIds, startDate, endDate);
		} else {
			return queryPerfByIdAndSize(appIds, agentIds, startDate, endDate, 1000 * 60 * 60L);
		}
	}

	/**
	 * 地域/错误码维度查询单独增加接口 这里不再提供。
	 */
	@Override
	public List<PerformanceView> queryPerformanceByDimensionType(String applicationId, String dimensionType, int top,
			String orderBy, long startDate, long endDate, String panelId) {
		List<PerformanceView> topPerf = null;
		// 根据applicationId查询应用系统，再查询整个应用系统下的所有节点
		ApplicationSystem system = applicationService.getApplicationById(applicationId);
		SearchCondition con = SearchCondition.newBuilder().add("timestamp", SearchConditionComparator.LEFTBETWEEN,
				startDate, endDate);
		con.add("applicationId", SearchConditionComparator.IN, applicationId);
		con.add(dimensionType, SearchConditionComparator.EXISTS);
		if (StringUtils.isBlank(orderBy))
			orderBy = "system/response_time/avg";
		// size 分类暂时不会超过50
		TermsBuilder aggs = AggregationUtil.getTerms("dimensionType", dimensionType, orderBy, false, 50);
		if ("system".equals(panelId)) {
			aggs.subAggregation(AggregationUtil.getAvg("system/response_time/avg", "elapsed")); // 平均响应时间
			aggs.subAggregation(AggregationUtil.getFilter("system/error/count", "transactionFlag", new String[]{"1", "3"})); // 错误数
			aggs.subAggregation(AggregationUtil.getCount("system/throughput/total", "_index")); // 吞吐量
			aggs.subAggregation(AggregationUtil.getTerms("system/user/unique", "remoteAddr")); // 独立用户数
			aggs.subAggregation(AggregationUtil.getRangeFilter("system/apdex/satisfied", "elapsed", 0, system.getApdex_t(), true, false)); // 满意数
			aggs.subAggregation(AggregationUtil.getRangeFilter("system/apdex/tolerate", "elapsed", system.getApdex_t(), 4 * system.getApdex_t(), true, false)); // 不满意数
		} else {
			con.add("tradeTypeName", SearchConditionComparator.EXISTS);
			aggs.subAggregation(AggregationUtil.getAvg("business/response_time/avg", "elapsed"));
			aggs.subAggregation(AggregationUtil.getFilter("business/error/count", "transactionFlag", new String[]{"1", "3"}));
			aggs.subAggregation(AggregationUtil.getSum("business/throughput/total", "_index"));
			aggs.subAggregation(AggregationUtil.getAvg("business/user/unique", "remoteAddr"));
			aggs.subAggregation(AggregationUtil.getRangeFilter("business/apdex/satisfied", "elapsed", 0, system.getApdex_t(), true, false));
			aggs.subAggregation(AggregationUtil.getRangeFilter("business/apdex/tolerate", "elapsed", system.getApdex_t(), 4 * system.getApdex_t(), true, false));
		}
		
		try {
			List<PerformanceView> staticPerf = queryPerfService.queryAggregationTermsPerf(con.add(aggs));
			// 构造一个新的top集合
			topPerf = new ArrayList<PerformanceView>();
			// 构造一个others
			PerformanceView others = null;
			int othersSize = 1;
			// 添加维度名称
			if (staticPerf != null && !staticPerf.isEmpty()) {
				for (int i = 0; i < staticPerf.size(); i++) {
					PerformanceView dimensionPerf = staticPerf.get(i);
					if ("system".equals(panelId)) {
						dimensionPerf.put("system/score/apdex", (dimensionPerf.get("system/apdex/satisfied") == null
								|| dimensionPerf.get("system/apdex/tolerate") == null) ? "0.0" : (long) (100
								* (Long.parseLong(dimensionPerf.get("system/apdex/satisfied").toString())
										+ Long.parseLong(dimensionPerf.get("system/apdex/tolerate").toString()) * 0.5)
								/ Long.parseLong(dimensionPerf.get("system/throughput/total").toString())));
					} else {
						dimensionPerf.put("business/score/apdex", (dimensionPerf.get("business/apdex/satisfied") == null
								|| dimensionPerf.get("business/apdex/tolerate") == null) ? "0.0"
										: (long) (100 * (Long.parseLong(dimensionPerf.get("business/apdex/satisfied").toString())
														+ Long.parseLong(dimensionPerf.get("business/apdex/tolerate").toString()) * 0.5)
												/ Long.parseLong(dimensionPerf.get("business/throughput/total").toString())));
					}
					String dimension = dimensionPerf.getDimensionType();
					if ("others".equals(dimension) || i >= top) {
						if (others == null) {
							others = dimensionPerf.copy();
						} else {
							othersSize++;
							if ("system".equals(panelId)) {
								if (others.get("system/response_time/avg") != null) {
									others.addLong(dimensionPerf, "system/response_time/avg");
								}
								if (others.get("system/score/apdex") != null) {
									others.addLong(dimensionPerf, "system/score/apdex");
								}
								if (others.get("system/user/unique") != null) {
									others.addLong(dimensionPerf, "system/user/unique");
								}
								if (others.get("system/error/count") != null) {
									others.addLong(dimensionPerf, "system/error/count");
								}
								if (others.get("system/throughput/total") != null) {
									others.addLong(dimensionPerf, "system/throughput/total");
								}
							} else {
								if (others.get("business/response_time/avg") != null) {
									others.addLong(dimensionPerf, "business/response_time/avg");
								}
								if (others.get("business/score/apdex") != null) {
									others.addLong(dimensionPerf, "business/score/apdex");
								}
								if (others.get("business/user/unique") != null) {
									others.addLong(dimensionPerf, "business/user/unique");
								}
								if (others.get("business/error/count") != null) {
									others.addLong(dimensionPerf, "business/error/count");
								}
								if (others.get("business/throughput/total") != null) {
									others.addLong(dimensionPerf, "business/throughput/total");
								}
							}
						}
						continue;
					}
					topPerf.add(dimensionPerf);
				}
				
				if ("system".equals(panelId)) {
					topPerf = topPerf.stream()
							.sorted((p1, p2) -> ((Long) (p2.getLong("system/user/unique")))
									.compareTo((Long) (p1.getLong("system/user/unique"))))
							.collect(Collectors.toList());
				} else {
					topPerf = topPerf.stream()
							.sorted((p1, p2) -> ((Long) (p2.getLong("business/user/unique")))
									.compareTo((Long) (p1.getLong("business/user/unique"))))
							.collect(Collectors.toList());
				}
 
				if (others != null) {
					if ("system".equals(panelId)) {
						Double concurrentUser = Math.ceil(others.getDouble("system/user/unique") / othersSize);
						others.put("system/user/unique", concurrentUser.longValue());
						others.put("system/score/apdex", others.getLong("system/score/apdex") / othersSize);
						others.put("system/response_time/avg", others.getLong("system/response_time/avg") / othersSize);
					} else {
						if (others.get("business/user/unique") != null) {
							Double concurrentUser = Math.ceil(others.getDouble("business/user/unique") / othersSize);
							others.put("business/user/unique", concurrentUser.longValue());
						}
						if (others.get("business/score/apdex") != null) {
							others.put("business/score/apdex", others.getLong("business/score/apdex") / othersSize);
						}
						if (others.get("business/response_time/avg") != null) {
							others.put("business/response_time/avg", others.getLong("business/response_time/avg") / othersSize);
						}
					}
					others.put("dimensionType", "其他");//TODO
					if (topPerf.size() == top)
						topPerf = topPerf.subList(0, top - 1);
					topPerf.add(others);
				}

			} else {
				if ("browser".equals(dimensionType))
					topPerf = BrowserManager.getInstance().getNullPerf();
				if ("os".equals(dimensionType))
					topPerf = OSManager.getInstance().getNullPerf();
				if ("device".equals(dimensionType))
					topPerf = DeviceManager.getInstance().getNullPerf();
				// 返回空性能的维度数据，默认只取top
				if (topPerf != null && topPerf.size() > 0) {
					if (topPerf.size() > top)
						topPerf = topPerf.subList(0, top);
				}
			}
		} catch (ApmDataIndicatorException e) {
			ExceptionUtils.error(log, "查询性能数据失败", e);
		}
		return topPerf;
	}

	@Override
	public AreaPerformance queryPerformanceByArea(String appId, String sortField, long startDate, long endDate) {
		AreaPerformance areaPerformance = new AreaPerformance();
		List<PerformanceView> areaPerf = new ArrayList<PerformanceView>();
		ApplicationSystem system = applicationService.getApplicationById(appId);
		String areaCode = null;
		try {
			com.broada.apm.entity.ApplicationSystem application = appLogic.getApplicationInfoById(appId);

			Area area = AreaManager.getInstance().getAreaByCode(application.getAreaCode());
			// 市辖区特殊处理
			Area municArea = AreaManager.getInstance().MunicipalityCode(application.getAreaCode());
			if (municArea != null) {
				areaCode = municArea.getCode();
				areaPerformance.setMapType(municArea.getName());
			} else {
				areaCode = area.getCode();
				areaPerformance.setMapType(area.getName());
			}
			List<Area> areas = AreaManager.getInstance().queryAreaByCode(areaCode);
			// 遍历获取下级及下下级等汇总数据。
			String orderDirection = "asc";
			for (Area _area : areas) {
				SearchCondition con = SearchCondition.newBuilder().add("startTime",
						SearchConditionComparator.LEFTBETWEEN, startDate, endDate).add("dimensionId",
						SearchConditionComparator.PREFIX, _area.getCode()).add(AggregationUtil.getCount("count", "_id"));
				
				if("system/response_time/avg".equals(sortField)){
					con.add(AggregationUtil.getAvg(sortField, "elapsed"));
				}else if( "system/success/rate".equals(sortField)){
					con.add(AggregationUtil.getSum("system/throughput/total", "_id"));
					con.add(AggregationUtil.getFilter("system/error/count", "transactionFlag", new String[]{"1", "3"}));
				}else if("system/score/apdex".equals(sortField)){
					con.add(AggregationUtil.getRangeFilter("system/apdex/satisfied", "elapsed", 0, system.getApdex_t(), true, false));
					con.add(AggregationUtil.getRangeFilter("system/apdex/tolerate", "elapsed", system.getApdex_t(), 4 * system.getApdex_t(), true, false));
				}else if("system/error/count".equals(sortField)){
					con.add(AggregationUtil.getFilter("system/error/count", "transactionFlag", new String[]{"1", "3"}));
				}else if("system/throughput/total".equals(sortField)){
					con.add(AggregationUtil.getSum(sortField, "_id"));
				}else if("system/user/unique".equals(sortField)){
					con.add(AggregationUtil.getSum(sortField, "remoteAddr"));
				}else if("business/response_time/avg".equals(sortField)){
					con.add("tradeType", SearchConditionComparator.EXISTS);
					con.add(AggregationUtil.getAvg(sortField, "elapsed"));
				}else if( "business/success/rate".equals(sortField)){
					con.add("tradeType", SearchConditionComparator.EXISTS);
					con.add(AggregationUtil.getSum("system/throughput/total", "_id"));
					con.add(AggregationUtil.getFilter("system/error/count", "transactionFlag", new String[]{"1", "3"}));
				}else if("business/score/apdex".equals(sortField)){
					con.add("tradeType", SearchConditionComparator.EXISTS);
					con.add(AggregationUtil.getRangeFilter("system/apdex/satisfied", "elapsed", 0, system.getApdex_t(), true, false));
					con.add(AggregationUtil.getRangeFilter("system/apdex/tolerate", "elapsed", system.getApdex_t(), 4 * system.getApdex_t(), true, false));
				}else if("business/error/count".equals(sortField)){
					con.add("tradeType", SearchConditionComparator.EXISTS);
					con.add(AggregationUtil.getFilter("system/error/count", "transactionFlag", new String[]{"1", "3"}));
				}else if("business/throughput/total".equals(sortField)){
					con.add("tradeType", SearchConditionComparator.EXISTS);
					con.add(AggregationUtil.getSum(sortField, "_id"));
				}else if("business/user/unique".equals(sortField)){
					con.add("tradeType", SearchConditionComparator.EXISTS);
					con.add(AggregationUtil.getSum(sortField, "remoteAddr"));
				}
				
				PerformanceView citysPerf = queryPerfService.queryAggregationSingelePerfByDimensionType(appId, con);
				if (citysPerf != null && citysPerf.getLong("count") > 0) {
					citysPerf.put("name", _area.getName());
					areaPerf.add(citysPerf);
				}
			}
			
			if ("system/response_time/avg".equals(sortField) || "business/response_time/avg".equals(sortField) 
					|| "system/throughput/total".equals(sortField) || "business/throughput/total".equals(sortField)
					|| "system/error/count".equals(sortField) || "business/error/count".equals(sortField)) {
				orderDirection = "desc";
			}
			
			List<PerformanceView> sortPerfs = perfSortByCondition(areaPerf, sortField, orderDirection);
			if ("system/success/rate".equals(sortField) && sortPerfs != null) {
				for (PerformanceView perfView : sortPerfs) {
					perfView.put("system/success/rate", (perfView.get("system/error/count") == null || perfView.get("system/throughput/total") == null || perfView.getLong("system/throughput/total") == 0)
							? "0.0" : (long)((100.00 - 100.00 * perfView.getLong("system/error/count") / perfView.getLong("system/throughput/total")) * 100) / 100.00);
				}
			}
			if("business/success/rate".equals(sortField) && sortPerfs != null){
				for (PerformanceView perfView : sortPerfs) {
					perfView.put("business/success/rate", (perfView.get("business/error/count") == null || perfView.get("business/throughput/total") == null || perfView.getLong("business/throughput/total") == 0)
							? "0.0" : (long)((100.00 - 100.00 * perfView.getLong("business/error/count") / perfView.getLong("business/throughput/total")) * 100) / 100.00);
				}
			}
			if ("system/score/apdex".equals(sortField) && sortPerfs != null) {
				for (PerformanceView perfView : sortPerfs) {
					perfView.put("system/score/apdex", (perfView.get("system/apdex/satisfied") == null
							|| perfView.get("system/apdex/tolerate") == null) ? "0.0" : (long) (100
							* (Long.parseLong(perfView.get("system/apdex/satisfied").toString())
									+ Long.parseLong(perfView.get("system/apdex/tolerate").toString()) * 0.5)
							/ Long.parseLong(perfView.get("system/throughput/total").toString())));
				}
			}
			if ("business/score/apdex".equals(sortField) && sortPerfs != null) {
				for (PerformanceView perfView : sortPerfs) {
					perfView.put("business/score/apdex", (perfView.get("business/apdex/satisfied") == null
							|| perfView.get("business/apdex/tolerate") == null) ? "0.0" : (long) (100
							* (Long.parseLong(perfView.get("business/apdex/satisfied").toString())
									+ Long.parseLong(perfView.get("business/apdex/tolerate").toString()) * 0.5)
							/ Long.parseLong(perfView.get("business/throughput/total").toString())));
				}
			}
			areaPerformance.setPerformances(sortPerfs);
		} catch (ApmDataIndicatorException e) {
			ExceptionUtils.error(log, "按区域查询性能失败", e);
		}
		return areaPerformance;
	}
	
	private List<PerformanceView> perfSortByCondition(List<PerformanceView> staticPerf, String orderBy,
			String orderDirection) {

		List<OrderClass> list = new ArrayList<OrderClass>();
		List<PerformanceView> staticPerf2 = new ArrayList<PerformanceView>();
		for (int i = 0; i < staticPerf.size(); i++) {
			OrderClass order = new OrderClass();
			PerformanceView performanceView = staticPerf.get(i);
			if ("ip".equals(orderBy)) {
				orderBy = "ip_port";
			}
			if (performanceView.get(orderBy) != null) {
				if (performanceView.get(orderBy).toString().equals("NaN")) {
					performanceView.put(orderBy, 0.0);
				}
				if ("ip_port".equals(orderBy)) {
					order.setOrder(Double.parseDouble(String.valueOf(IPUtils.toLong(performanceView.get("ip_port").toString().split(":")[0]))));
				} else {
					order.setOrder(Double.parseDouble(performanceView.get(orderBy).toString()));
				}
			}else{
				order.setOrder(Double.parseDouble(String.valueOf(IPUtils.toLong(performanceView.get("ip_port").toString()))));
			}
			order.setPerformanceView(performanceView);
			list.add(order);
		}

		if (orderDirection.equals("asc")) {
			Collections.sort(list, new Comparator<OrderClass>() {
				public int compare(OrderClass arg0, OrderClass arg1) {
					return arg0.getOrder().compareTo(arg1.getOrder());
				}
			});
		} else {
			Collections.sort(list, new Comparator<OrderClass>() {
				public int compare(OrderClass arg0, OrderClass arg1) {
					return arg1.getOrder().compareTo(arg0.getOrder());
				}
			});
		}

		for (int i = 0; i < list.size(); i++) {
			OrderClass orderClass = list.get(i);
			staticPerf2.add(i, orderClass.getPerformanceView());
		}
		return staticPerf2;

	}

	/**
	 * 查询回溯模式下应用系统和节点的性能信息，粒度为60分钟
	 * 
	 * @param appIds 系统id
	 * @param nodeIds 系统会有多个节点
	 * @param startDate
	 * @param endDate
	 * @param size 粒度 默认60分钟
	 * @return
	 */
	public List<PerformanceGroup> queryPerfByIdAndSize(String[] appIds, String[] agentIds, long startDate, long endDate,
			long size) {
		List<PerformanceGroup> pairs = new ArrayList<PerformanceGroup>();
		String appId = "";
		if (appIds != null && appIds.length > 0) {
			appId = appIds[0];
		}
		try {
			DateHistogramBuilder dataCon = (DateHistogramBuilder) AggregationUtil.getDateHistogram("agentStartTime",
					"agentStartTime", size);
			dataCon.subAggregation(AggregationUtil.getSum("system/throughput/total", "_id"))
					.subAggregation(AggregationUtil.exitsFilters("business/throughput/total", "tradeTypeName"));
			
			if (appIds != null && (ArrayUtils.isEmpty(agentIds))) {
				for (String id : appIds) {
					SearchCondition conn = buildSearchConditionByLong(startDate, endDate, "applicationId",
							SearchConditionComparator.EQUALS, id, Integer.MAX_VALUE).add(dataCon);
					List<PerformanceView> perfs = queryPerfService.queryAggregationDateHistogramPerf(id, conn);
					pairs.add(new PerformanceGroup(id, perfs, perfs.isEmpty() ? null : perfs.get(perfs.size() - 1)));
				}
			} else {
				if (agentIds != null) {
					for (String id : agentIds) {
						SearchCondition conn = buildSearchConditionByLong(startDate, endDate, "agentId",
								SearchConditionComparator.EQUALS, id, Integer.MAX_VALUE).add(dataCon);
						List<PerformanceView> perfs = queryPerfService.queryAggregationDateHistogramPerf(appId, conn);
						pairs.add(new PerformanceGroup(id, perfs, perfs.isEmpty() ? null : perfs.get(perfs.size() - 1)));
					}
				}
			}
		} catch (ApmDataIndicatorException e) {
			ExceptionUtils.error(log, "查询分组性能数据失败", e);
		}
		return pairs;
	}

	/**
	 * 查询最近一小时的系统和节点的性能信息
	 * 
	 * @param appIds
	 * @param nodeIds
	 * @param startDate
	 * @param endDate
	 * @return
	 */
	public List<PerformanceGroup> queryPerfById(String[] appIds, String[] agentIds, long startDate, long endDate) {
		String appId = "";
		if (ArrayUtils.isNotEmpty(appIds)) {
			appId = appIds[0];
		}
		List<PerformanceGroup> pairs = new ArrayList<PerformanceGroup>();
		try {
			if (ArrayUtils.isNotEmpty(appIds) && ArrayUtils.isNotEmpty(agentIds) &&  "default".equals(appIds[0])) {
				SearchCondition con = SearchCondition.newBuilder();
				con.add("agentStartTime", SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
				con.add("agentId", SearchConditionComparator.IN, agentIds);
				con.setPageSize(60);
				PagingList<PerformanceView> perf = queryPerfService.queryPerf(con);
				pairs.add(new PerformanceGroup(agentIds[0], perf.getData(), perf.getData().isEmpty() ? null : perf.getData().get(perf.getData().size() - 1)));
				
			} else if (ArrayUtils.isNotEmpty(appIds) && (ArrayUtils.isEmpty(agentIds))) {
				for (String id : appIds) {
					SearchCondition conn = buildSearchConditionByLong(startDate, endDate, "applicationId",
							SearchConditionComparator.EQUALS, id, 60);
					PagingList<PerformanceView> perfs = queryPerfService.queryPerf(conn);
					
					// 交易总览页面交易总量用
					Calendar today = Calendar.getInstance();  
					today.set(Calendar.HOUR_OF_DAY, 0);
			        today.set(Calendar.MINUTE, 0);  
			        today.set(Calendar.SECOND, 0);  
			        today.set(Calendar.MILLISECOND, 0);
			        
			        Calendar nextDay = Calendar.getInstance();
			        nextDay.set(Calendar.HOUR_OF_DAY, 0);
			        nextDay.set(Calendar.MINUTE, 0);
			        nextDay.set(Calendar.SECOND, 0);
			        nextDay.set(Calendar.MILLISECOND, 0);
			        nextDay.add(Calendar.DAY_OF_MONTH, 1);
					conn = buildSearchConditionByLong(today.getTime().getTime(), nextDay.getTime().getTime(), "applicationId",
							SearchConditionComparator.EQUALS, id, 60);
					TermsBuilder aggs = AggregationUtil.getTerms("applicationId", "applicationId");
					aggs.subAggregation(AggregationUtil.getSum("business/throughput/total", "_id"));
					List<PerformanceView> lastPerf = queryPerfService.queryAggregationTermsPerf(id, RESOURCE_TYPE.APPLICATION, conn.add(aggs));
					
					pairs.add(new PerformanceGroup(id, perfs.getData(), lastPerf.isEmpty() ? null : lastPerf.get(0)));
				}
			} else {
				if (agentIds != null) {
					for (String id : agentIds) {
						PagingList<PerformanceView> perfs = new PagingList<PerformanceView>();
						SearchCondition conn = buildSearchConditionByLong(startDate, endDate, "agentId",
								SearchConditionComparator.EQUALS, id, 60);
						perfs = queryPerfService.queryPerf(conn);
						pairs.add(new PerformanceGroup(id, perfs.getData(), perfs.getData().isEmpty() ? null : perfs.getData().get(perfs.getData().size() - 1)));
					}
				}
			}
		} catch (Exception e) {
			ExceptionUtils.error(log, "查询性能数据失败", e);
		}
		return pairs;
	}

	/**
	 * 实时查询交易详细信息
	 * 
	 * @param appId
	 * @param resId 交易id
	 * @param lastDate 最后一分钟
	 * @return
	 */
	@Override
	public PerformanceDetail queryDetailsPerformanceByLastDate(String appId, String resId, Date lastDate) {
		Date end = DateUtils.addYears(lastDate, 1);
		return queryDetailsPerformanceByKeyTrade(appId, resId, lastDate, end);
	}

	/**
	 * 查询最近一小时交易详情
	 * 
	 * @param appId
	 * @param resId
	 * @param top 交易详情页面错误码显示前top条
	 * @return
	 */
	@Override
	public PerformanceDetail
			queryDetailsPerformanceByKeyTrade(String appId, String resId, Date startDate, Date endDate) {
		PerformanceDetail detail = new PerformanceDetail();
		try {
			// 查询性能
			SearchCondition conPerf = buildSearchCondition(startDate, endDate, "resourceId",
					SearchConditionComparator.EQUALS, resId, 60);
			PagingList<PerformanceView> perfs = queryPerfService.queryPerf(conPerf
					.add("dimensionType", SearchConditionComparator.ISNULL));
			detail.setPerformances(perfs.getData());
			// 查询错误码
			if (DateUtils.isSameDay(DateUtils.addYears(startDate, 1), endDate)) {
				endDate = startDate;
				startDate = DateUtils.addHours(endDate, -60);
			}
			SearchCondition conError = buildSearchCondition(startDate, endDate, "resourceId",
					SearchConditionComparator.EQUALS, resId, Integer.MAX_VALUE);
			// 默认获取所有的错误码，应该不会大于1000个
			TermsBuilder term = AggregationUtil.getTerms("responseCode", "dimensionId", "count", false, 1000);
			term.subAggregation(AggregationUtil.getCount("count", "id"));
			List<Map<String, Object>> errorList = queryPerfService.queryErrorCodeCount( conError.add(term));
			List<ErrorCodeCount> errorCodeCounts = new ArrayList<ErrorCodeCount>();
			for (Map<String, Object> map : errorList) {
				errorCodeCounts.add(JsonUtil.map2obj(map, ErrorCodeCount.class));
			}
			detail.setErrorCode(errorCodeCounts);
		} catch (ApmDataIndicatorException e) {
			log.error("查询交易详情性能失败", e);
		}
		return detail;
	}

	private SearchCondition buildSearchCondition(Date startDate, Date endDate, String type,
			SearchConditionComparator condition, String id, int pageSize) {
		SearchCondition con = SearchCondition.newBuilder().add("startTime", SearchConditionComparator.LEFTBETWEEN,
				startDate, endDate);
		con.setPageSize(pageSize);
		con.setSortField("startTime");
		con.setSortDirection("ASC");
		if (type == null || "".equals(type.trim())) {

		} else {
			con.add(type, condition, id);
		}
		return con;
	}
	
	private SearchCondition buildSearchConditionByLong(long startDate, long endDate, String type,
			SearchConditionComparator condition, String id, int pageSize) {
		SearchCondition con = SearchCondition.newBuilder().add("timestamp", SearchConditionComparator.LEFTBETWEEN,
				startDate, endDate);
		con.setPageSize(pageSize);
		con.setSortField("timestamp");
		con.setSortDirection("ASC");
		if (type == null || "".equals(type.trim())) {

		} else {
			con.add(type, condition, id);
		}
		return con;
	}

	/**
	 * 节点详情页面连接数性能
	 */
	@Override
	public PagingList<PerformanceView> queryPerformanceByConnection(String appId, String nodeId, Date startDate,
			Date endDate, int pageIndex, int pageSize) {
		PagingList<PerformanceView> perfsList = new PagingList<PerformanceView>();
		try {
			String[] clusIds = isCluster(nodeId);
			// 查询节点获取节点名称
			List<Service> servs = serverLogic.queryServicesByIds(clusIds);

			Map<String, String> servName = new HashMap<String, String>();
			for (Service ser : servs) {
				String name = ser.getName();
				if (name == null || "".equals(name))
					name = ser.getIp() + ":" + ser.getPort();
				servName.put(ser.getId(), name);
			}
			// 查询节点性能
			SearchCondition con = SearchCondition.newBuilder().add("timestamp", SearchConditionComparator.ALLBETWEEN,
					startDate, endDate).add("dimensionType", SearchConditionComparator.ISNULL)
					.setPageSize(servs.size());
			if (!ArrayUtils.isEmpty(clusIds))
				con.add("serviceId", SearchConditionComparator.IN, clusIds);
			perfsList = queryPerfService.queryPerf(con);

			for (int i = 0; i < perfsList.size(); i++) {
				perfsList.get(i).setName(servName.get(perfsList.get(i).getServiceId()));
			}
		} catch (ApmDataIndicatorException e) {
			log.error("按连接数查询接入性能", e);
		}
		return perfsList;
	}

	@Override
	public List<PerformanceView> queryPerformanceByPage(String appId, int top, String sortField, long startDate,
			long endDate) {
		List<PerformanceView> statisPerf = new ArrayList<PerformanceView>();
		List<PerformanceView> tmpStatisPerf = new ArrayList<PerformanceView>();
		try {
			ApplicationSystem system = applicationService.getApplicationById(appId);
			List<Service> serviceList = serverLogic.queryServiceByAppId(appId);
			List<String> serviceIds = new ArrayList<String>();
			for (Service service : serviceList) {
				if ((service.getProtocol() == 80 || service.getAutoDiscoveryProtocol() == 80) && service.getNodeType() != 2) {
					serviceIds.add(service.getId());
				}
			}
			if (serviceIds.isEmpty()) {
				return tmpStatisPerf;
			}
			// 普通查询条件
			SearchCondition condition = SearchCondition.newBuilder().add("agentStartTime",
					SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
			String[] serviceIdsArray = (String[])serviceIds.toArray(new String[serviceIds.size()]);
			condition.add("applicationId", SearchConditionComparator.IN, appId);
			if (sortField.startsWith("system")) {
				condition.add("tradeTypeName", SearchConditionComparator.ISNULL);
			}
			// 统计查询条件
			TermsBuilder aggs = AggregationUtil.getTerms("resourceId", "rpc", sortField, ("system/score/apdex"
					.equals(sortField) || "system/success/rate".equals(sortField) || "business/score/apdex"
					.equals(sortField) || "business/success/rate".equals(sortField)), top);
			
			if ("system/response_time/avg".equals(sortField) || "business/response_time/avg".equals(sortField))
				aggs.subAggregation(AggregationUtil.getAvg(sortField, "elapsed"));
			if ("system/user/unique".equals(sortField) || "business/user/unique".equals(sortField))
				aggs.subAggregation(AggregationUtil.getTerms(sortField, "remoteAddr"));
			if ("system/error/count".equals(sortField) || "business/error/count".equals(sortField))
				aggs.subAggregation(AggregationUtil.getFilter(sortField, "transactionFlag", new String[]{"1", "3"}));
			if ("system/throughput/total".equals(sortField) || "business/throughput/total".equals(sortField))
				aggs.subAggregation(AggregationUtil.getSum(sortField, "_index"));
			
			if ("system/success/rate".equals(sortField)) {
				aggs.subAggregation(AggregationUtil.getSum("system/throughput/total", "_index"));
				aggs.subAggregation(AggregationUtil.getFilter("system/error/count", "transactionFlag", new String[]{"1", "3"}));
			}
			if ("business/success/rate".equals(sortField)) {
				aggs.subAggregation(AggregationUtil.getSum("system/throughput/total", "_index"));
				aggs.subAggregation(AggregationUtil.getFilter("system/error/count", "transactionFlag", new String[]{"1", "3"}));
			}
			if ("system/score/apdex".equals(sortField)) {
				aggs.subAggregation(AggregationUtil.getRangeFilter("system/apdex/satisfied", "elapsed", 0, system.getApdex_t(), true, false));
				aggs.subAggregation(AggregationUtil.getRangeFilter("system/apdex/tolerate", "elapsed", system.getApdex_t(), 4 * system.getApdex_t(), true, false));
			}
			if ("business/score/apdex".equals(sortField)) {
				aggs.subAggregation(AggregationUtil.getRangeFilter("system/apdex/satisfied", "elapsed", 0, system.getApdex_t(), true, false));
				aggs.subAggregation(AggregationUtil.getRangeFilter("system/apdex/tolerate", "elapsed", system.getApdex_t(), 4 * system.getApdex_t(), true, false));
			}
			condition.add(aggs);

			statisPerf = queryPerfService.queryAggregationTermsPerf(condition);
			if ("system/success/rate".equals(sortField) && statisPerf != null) {
				for (PerformanceView perfView : statisPerf) {
					perfView.put("system/success/rate", 
							(perfView.get("system/error/count") == null || perfView.get("system/throughput/total") == null || perfView.getLong("system/throughput/total") == 0)
							? "0.0" : (long)((100.00 - 100.00 * perfView.getLong("system/error/count") / perfView.getLong("system/throughput/total")) * 100) / 100.00);
				}
			}
			if ("business/success/rate".equals(sortField) && statisPerf != null) {
				for (PerformanceView perfView : statisPerf) {
					perfView.put("business/success/rate", 
							(perfView.get("business/error/count") == null || perfView.get("business/throughput/total") == null || perfView.getLong("business/throughput/total") == 0)
							? "0.0" : (long)((100.00 - 100.00 * perfView.getLong("business/error/count") / perfView.getLong("business/throughput/total")) * 100) / 100.00);
				}
			}
			if ("system/score/apdex".equals(sortField)) {
				for (PerformanceView perfView : statisPerf) {
					perfView.put("system/score/apdex", (perfView.get("system/apdex/satisfied") == null
							|| perfView.get("system/apdex/tolerate") == null) ? "0.0" : (long) (100
							* (Long.parseLong(perfView.get("system/apdex/satisfied").toString())
									+ Long.parseLong(perfView.get("system/apdex/tolerate").toString()) * 0.5)
							/ Long.parseLong(perfView.get("system/throughput/total").toString())));
				}
			}
			if ("business/score/apdex".equals(sortField)) {
				for (PerformanceView perfView : statisPerf) {
					perfView.put("business/score/apdex", (perfView.get("business/apdex/satisfied") == null
							|| perfView.get("business/apdex/tolerate") == null) ? "0.0" : (long) (100
							* (Long.parseLong(perfView.get("business/apdex/satisfied").toString())
									+ Long.parseLong(perfView.get("business/apdex/tolerate").toString()) * 0.5)
							/ Long.parseLong(perfView.get("business/throughput/total").toString())));
				}
			}
		} catch (Exception e) {
			ExceptionUtils.error(log, "queryPerformanceByPage查询错误", e);
		}
		return tmpStatisPerf;
	}

	@Override
	public List<PerformanceView> queryPerformanceByPageId(String appId, String resourceId, String field,
			String size, Date startDate, Date endDate) {
		if(size == null) {
			PagingList<PerformanceView> perfs = new PagingList<PerformanceView>();
			SearchCondition condition = SearchCondition.newBuilder().add("resourceId", SearchConditionComparator.EQUALS,
					resourceId).add("timestamp", SearchConditionComparator.LEFTBETWEEN, startDate, endDate).add(
					"dimensionId", SearchConditionComparator.ISNULL).setSortField("timestamp").setPageSize(2000);
			try {
				perfs = queryPerf(appId, RESOURCE_TYPE.RESOURCE, condition);
			} catch (ApmDataIndicatorException e) {
				ExceptionUtils.error(log, "queryPerformanceByPageId 失败", e);
			}
			return perfs.getData();
		} else {
			long time = 1000 * 60L;
			if("hour".equals(size))
				time = 1000 * 60 * 60L;
			else if("day".equals(size))
				time = 1000 * 60 * 60 *24L;
			else if("minutes".equals(size))
				time = 1000 * 60 * 15L;
			DateHistogramBuilder dataCon = (DateHistogramBuilder) AggregationUtil.getDateHistogram("timestamp",
					"timestamp", time);
			RESOURCE_TYPE type = RESOURCE_TYPE.RESOURCE;
			if(field != null) {
				if ("system/response_time/avg".equals(field) || "business/response_time/avg".equals(field))
					dataCon.subAggregation(AggregationUtil.getAvg(field, field));
				if ("system/score/apdex".equals(field) || "system/success/rate".equals(field)
						|| "business/score/apdex".equals(field) || "business/success/rate".equals(field))
					dataCon.subAggregation(AggregationUtil.getAvg(field, field));
				if ("system/error/count".equals(field) || "system/throughput/total".equals(field)
						|| "business/error/count".equals(field) || "business/throughput/total".equals(field))
					dataCon.subAggregation(AggregationUtil.getSum(field, field));
				if (field.startsWith("business")) {
					type = RESOURCE_TYPE.KEY_RESOURCE;
				}
			} else {//defalut
				dataCon.subAggregation(AggregationUtil.getAvg("system/response_time/avg", "system/response_time/avg"))
						.subAggregation(AggregationUtil.getAvg("system/score/apdex", "system/score/apdex"))
						.subAggregation(AggregationUtil.getAvg("system/success/rate", "system/success/rate"))
						.subAggregation(AggregationUtil.getSum("system/error/count", "system/error/count"))
						.subAggregation(AggregationUtil.getSum("system/throughput/total", "system/throughput/total"))
						.subAggregation(AggregationUtil.getAvg("business/response_time/avg", "business/response_time/avg"))
						.subAggregation(AggregationUtil.getAvg("business/score/apdex", "business/score/apdex"))
						.subAggregation(AggregationUtil.getAvg("business/success/rate", "business/success/rate"))
						.subAggregation(AggregationUtil.getSum("business/error/count", "business/error/count"))
						.subAggregation(AggregationUtil.getSum("business/throughput/total", "business/throughput/total"));
			}
			
			SearchCondition conn = buildSearchCondition(startDate, endDate, "resourceId",
					SearchConditionComparator.EQUALS, resourceId, Integer.MAX_VALUE).add("dimensionType",
					SearchConditionComparator.ISNULL).add(dataCon);
			List<PerformanceView> perfs = Lists.newArrayList();
			try {
				perfs = queryPerfService.queryAggregationDateHistogramPerf(appId,
						 conn);
			} catch (ApmDataIndicatorException e) {
				ExceptionUtils.error(log, "queryPerformanceByPageId 失败", e);
			}
			
			return perfs;
		}
	}

	/**
	 * 增加通用queryPerf查询接口
	 * 
	 * @param <T>
	 * @return
	 * @throws ApmDataIndicatorException
	 */
	private PagingList<PerformanceView> queryPerf(String applicationId, RESOURCE_TYPE type,
			SearchCondition searchCondition) throws ApmDataIndicatorException {
		return queryPerfService.queryPerf(searchCondition);
	}

	/**
	 * 如果不是集群则返回自身nodeId 否则返回集群关联nodeId
	 * 
	 * @param nodeId
	 * @return
	 */
	private String[] isCluster(String nodeId) {
		Service service = serverLogic.queryServiceById(nodeId);
		if (service != null && service.getNodeType() == 2)
			return service.getNodeIds();
		return new String[] {
			nodeId
		};
	}

	@Override
	public List<PerformanceView> queryPerformanceSubTotal(String appId, String type, String targetIds, Date startDate,
			Date endDate, String orderBy, boolean desc, String panelId) {
		List<PerformanceView> perfs = new ArrayList<PerformanceView>();
		List<PerformanceView> tmpPerfs = new ArrayList<PerformanceView>();
		SearchCondition con = SearchCondition.newBuilder();
		con.add("timestamp", SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
		con.add("dimensionType", SearchConditionComparator.ISNULL);
		RESOURCE_TYPE resourceType = RESOURCE_TYPE.SERVICE;
		TermsBuilder aggs = AggregationUtil.getTerms("serviceId", "serviceId", Integer.MAX_VALUE);
		String[] ids = null;
		
		if (targetIds.contains(",")) {
			ids = targetIds.split(",");
			if ("service".equals(type)) {
				con.add("serviceId", SearchConditionComparator.IN, ids);
			} else {
				con.add("resourceId", SearchConditionComparator.IN, ids);
				resourceType = RESOURCE_TYPE.RESOURCE;
				aggs = AggregationUtil.getTerms("resourceId", "resourceId", Integer.MAX_VALUE);
			}
		} else {
			ids = new String[1];
			ids[0] = targetIds;
			if ("service".equals(type)) {
				con.add("serviceId", SearchConditionComparator.IN, ids);
			} else {
				con.add("resourceId", SearchConditionComparator.IN, ids);
				resourceType = RESOURCE_TYPE.RESOURCE;
				aggs = AggregationUtil.getTerms("resourceId", "resourceId", Integer.MAX_VALUE);
			}
		}
		
		aggs.subAggregation(AggregationUtil.getSum("system/throughput/total", "system/throughput/total"));
		aggs.subAggregation(AggregationUtil.getSum("system/response_time/total", "system/response_time/total"));
		aggs.subAggregation(AggregationUtil.getSum("system/error/count", "system/error/count"));
		aggs.subAggregation(AggregationUtil.getSum("business/throughput/total", "business/throughput/total"));
		aggs.subAggregation(AggregationUtil.getSum("business/response_time/total", "business/response_time/total"));
		aggs.subAggregation(AggregationUtil.getSum("business/error/count", "business/error/count"));

		List<PerformanceView> staticPerf = null;
		try {
			staticPerf = queryPerfService.queryAggregationTermsPerf(appId, resourceType, con.add(aggs));
			perfs.addAll(staticPerf);
			tmpPerfs = convert(perfs, type, orderBy, desc);
		} catch (ApmDataIndicatorException e) {
			ExceptionUtils.error(log, "查询性能数据失败", e);
		}
		return tmpPerfs;
	}
	
	private List<PerformanceView> convert(List<PerformanceView> perf, String type, String orderBy, boolean desc) {
		List<PerformanceView> perfs2 = new ArrayList<PerformanceView>();
		if ("service".equals(type)) {
			Map<String, String> serviceMap = new HashMap<String, String>();
			for (PerformanceView performanceView : perf) {
				String serviceId = performanceView.getServiceId();
				if (!serviceMap.containsKey(serviceId)) {
					Service service = serverLogic.queryServiceById(serviceId);
					if (service != null)
						serviceMap.put(serviceId, service.getIp() + ":" + service.getPort());
				}
				performanceView.setIpPort(serviceMap.get(serviceId));
				performanceView.put("system/response_time/avg", (performanceView.get("system/response_time/total") == null || performanceView.get("system/throughput/total") == null || Double.parseDouble(performanceView.get("system/throughput/total").toString()) == 0) ? "-" : 
						Double.parseDouble(performanceView.get("system/response_time/total").toString())
								/ Double.parseDouble(performanceView.get("system/throughput/total").toString()));
				performanceView.put("system/success/rate", (performanceView.get("system/error/count") == null || performanceView.get("system/throughput/total") == null || Double.parseDouble(performanceView.get("system/throughput/total").toString()) == 0) ? "-" : BigDecimal.valueOf((Double.parseDouble(performanceView.get("system/throughput/total").toString())
						- Double.parseDouble(performanceView.get("system/error/count").toString()))
						/ Double.parseDouble(performanceView.get("system/throughput/total").toString()) * 100)
						   .setScale(2, BigDecimal.ROUND_DOWN).doubleValue());

				performanceView.put("business/response_time/avg", (performanceView.get("business/response_time/total") == null || performanceView.get("business/throughput/total") == null || Double.parseDouble(performanceView.get("business/throughput/total").toString()) == 0) ? "-" : 
						Double.parseDouble(performanceView.get("business/response_time/total").toString())
								/ Double.parseDouble(performanceView.get("business/throughput/total").toString()));
				performanceView.put("business/success/rate", (performanceView.get("business/error/count") == null || performanceView.get("business/throughput/total") == null || Double.parseDouble(performanceView.get("business/throughput/total").toString()) == 0) ? "-" : BigDecimal.valueOf((Double.parseDouble(performanceView.get("business/throughput/total").toString())
						- Double.parseDouble(performanceView.get("business/error/count").toString()))
						/ Double.parseDouble(performanceView.get("business/throughput/total").toString()) * 100)
						   .setScale(2, BigDecimal.ROUND_DOWN).doubleValue());
				performanceView.put("ip", performanceView.getIpPort().split(":")[0]);
			}
			String orderDirection = "asc";
			if (StringUtils.isBlank(orderBy)) {
				orderBy = "ip";
			}
			if (desc) {
				orderDirection = "desc";
			}
			if (perf.isEmpty() || StringUtils.isBlank(orderBy)) {
				perfs2 = perf;
			} else {
				perfs2 = perfSortByCondition(perf, orderBy, orderDirection);
			}
		} else if ("resourceId".equals(type)) {
			for (PerformanceView performanceView : perf) {
				Resource res = resourceService.getResourceById(performanceView.getResourceId());
				performanceView.setUri(res != null ? res.getUri() : null);
				performanceView.put("business/response_time/avg", (performanceView.get("business/response_time/total") == null || performanceView.get("business/throughput/total") == null || Double.parseDouble(performanceView.get("business/throughput/total").toString()) == 0) ? "-" : 
						Double.parseDouble(performanceView.get("business/response_time/total").toString())
								/ Double.parseDouble(performanceView.get("business/throughput/total").toString()));
				performanceView.put("business/success/rate", (performanceView.get("business/error/count") == null || performanceView.get("business/throughput/total") == null || Double.parseDouble(performanceView.get("business/throughput/total").toString()) == 0) ? "-" : 
						(Double.parseDouble(performanceView.get("business/throughput/total").toString())
								- Double.parseDouble(performanceView.get("business/error/count").toString()))
								/ Double.parseDouble(performanceView.get("business/throughput/total").toString()));
			}
			perfs2 = perf;
		}
		return perfs2;
	}
	
	@Override
	public List<PerformanceView> queryPerformance(List<String> appIds, String nodeId, String keyTradeId, String typeName, String channelName, long startDate,
			long endDate, Integer last, String field) {
		List<PerformanceView> pairs = new ArrayList<PerformanceView>();
		try {
			if (StringUtils.isNotBlank(channelName)) {
				SearchCondition con = SearchCondition.newBuilder();
				if (!appIds.isEmpty()) {
					con.add("applicationId", SearchConditionComparator.IN, appIds);
				}
				if (!appIds.isEmpty()) {
					con.add("agentId", SearchConditionComparator.EQUALS, nodeId);
				}
				con.add("agentStartTime", SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
				con.add("transactionChannelName", SearchConditionComparator.EQUALS, channelName);
				con.setPageSize(60);
				con.setSortField("agentStartTime");
				con.setSortDirection("ASC");
				
				PagingList<PerformanceView> perfs = queryPerfService.queryPerf(con);
				pairs = putStatus(perfs, pairs, last, field);
				return pairs;
			}
			if (StringUtils.isBlank(nodeId) && StringUtils.isBlank(keyTradeId)) {
				// 当nodeId和keyTradeId均为空时，查询的是应用系统的性能
				SearchCondition con = SearchCondition.newBuilder();
				if (!appIds.isEmpty()) {
					con.add("applicationId", SearchConditionComparator.IN, appIds);
				}
				con.add("agentStartTime", SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
				con.setPageSize(60);
				con.setSortField("agentStartTime");
				con.setSortDirection("DESC");
				
				PagingList<PerformanceView> perfs = queryPerfService.queryPerf(con);
				pairs = putStatus(perfs, pairs, last, field);
				return pairs;
			} else {
				// 否则查询的是节点性能或关键交易性能
				if (StringUtils.isNotBlank(nodeId) && StringUtils.isBlank(keyTradeId)) {
					PagingList<PerformanceView> perfs = new PagingList<PerformanceView>();
					SearchCondition con = SearchCondition.newBuilder();
					if (!appIds.isEmpty()) {
						con.add("applicationId", SearchConditionComparator.IN, appIds);
					}
					if (!appIds.isEmpty()) {
						con.add("agentId", SearchConditionComparator.EQUALS, nodeId);
					}
					con.add("agentStartTime", SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
					con.setPageSize(60);
					con.setSortField("agentStartTime");
					con.setSortDirection("DESC");
					
					perfs = queryPerfService.queryPerf(con);
					pairs = putStatus(perfs, pairs, last, field);
				}
				if (StringUtils.isNotBlank(keyTradeId)) {
					SearchCondition con = SearchCondition.newBuilder();
					if (!appIds.isEmpty()) {
						con.add("applicationId", SearchConditionComparator.IN, appIds);
					}
					if (!appIds.isEmpty()) {
						con.add("agentId", SearchConditionComparator.EQUALS, nodeId);
					}
					con.add("tradeTypeName", SearchConditionComparator.EQUALS, typeName);
					con.add("agentStartTime", SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
					con.setPageSize(60);
					con.setSortField("agentStartTime");
					con.setSortDirection("DESC");
					
					PagingList<PerformanceView> perfs = queryPerfService.queryPerf(con);
					pairs = putStatus(perfs, pairs, last, field);
				}
				return pairs;
			}
		} catch (Exception e) {
			ExceptionUtils.error(log, "查询性能数据失败", e);
		}
		return pairs;
	}
	
	private List<PerformanceView> putStatus(PagingList<PerformanceView> perfs, List<PerformanceView> pairs, Integer last, String field) {
		List<PerformanceView> temp = new ArrayList<PerformanceView>();
		String[] fields = null;
		try {
			if (StringUtils.isNotBlank(field)) {
				fields = field.contains(",") ? field.split(",") : new String[]{field};
			}
			if (!perfs.getData().isEmpty()) {
				if (last == null) {
					temp.add(perfs.getData().get(0));
				} else {
					if (perfs.getData().size() <= last) {
						temp.addAll(0, perfs.getData());
					} else {
						for (int i=0; i<last; i++) {
							temp.add(perfs.getData().get(i));
						}
					}
				}
				for (PerformanceView p : temp) {
					if (p.get("status") == null) {
						p.put("status", 0);
					}
					if (fields != null) {
						PerformanceView newPv = new PerformanceView();
						newPv.put("timestamp", p.get("agentStartTime"));
						newPv.put("status", p.get("status"));
						newPv.put("applicationId", p.get("applicationId"));
						for (String f : fields) {
							newPv.put(f, p.get(f));
						}
						pairs.add(newPv);
					} else {
						pairs.add(p);
					}
				}
			}
		} catch (Exception e) {
			ExceptionUtils.error(log, "查询性能数据失败", e);
		}
		return pairs;
	}

	@Override
	public PerformanceDetail queryDetailsPerformanceByTransaction(String appId, String tradeTypeName, Date startDate, Date endDate, String agentId) {
		PerformanceDetail detail = new PerformanceDetail();
		try {
			// 查询性能
			SearchCondition conPerf = buildSearchCondition(startDate, endDate, "tradeTypeName",
					SearchConditionComparator.EQUALS, tradeTypeName, 60);
			conPerf.add("agentId", SearchConditionComparator.EQUALS, agentId);
			
			PagingList<PerformanceView> perfs = queryPerfService.queryPerf(conPerf);
			detail.setPerformances(perfs.getData());
			
			// 查询错误码
			if (DateUtils.isSameDay(DateUtils.addYears(startDate, 1), endDate)) {
				endDate = startDate;
				startDate = DateUtils.addHours(endDate, -60);
			}
			SearchCondition conError = buildSearchCondition(startDate, endDate, "tradeTypeName",
					SearchConditionComparator.EQUALS, tradeTypeName, Integer.MAX_VALUE);
			// 默认获取所有的错误码，应该不会大于1000个
			TermsBuilder term = AggregationUtil.getTerms("responseCode", "dimensionId", "count", false, 1000);
			term.subAggregation(AggregationUtil.getCount("count", "id"));
			List<Map<String, Object>> errorList = queryPerfService.queryErrorCodeCount(conError.add(term));
			List<ErrorCodeCount> errorCodeCounts = new ArrayList<ErrorCodeCount>();
			for (Map<String, Object> map : errorList) {
				errorCodeCounts.add(JsonUtil.map2obj(map, ErrorCodeCount.class));
			}
			detail.setErrorCode(errorCodeCounts);
			
		} catch (ApmDataIndicatorException e) {
			log.error("查询交易详情性能失败", e);
		}
		return detail;
	}

	@Override
	public List<PerformanceView> queryAppPerfs(Set<String> applicationId, Date startDate, Date endDate) {
		SearchCondition con = SearchCondition.newBuilder();
		con.add("timestamp", SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
		
		TermsBuilder aggs = AggregationUtil.getTerms("applicationId", "applicationId");
		aggs.subAggregation(AggregationUtil.getSum("business/throughput/total", "business/throughput/total"));
		aggs.subAggregation(AggregationUtil.getSum("responseTime", "system/response_time/total"));
		aggs.subAggregation(AggregationUtil.getSum("business/error/count", "business/error/count"));
		aggs.subAggregation(AggregationUtil.getCount("count", "_index")); // 提供服务的总时间
		if (applicationId != null && !applicationId.isEmpty()) {
			con.add("applicationId", SearchConditionComparator.IN, applicationId.toArray(new String[applicationId.size()]));
			aggs.subAggregation(AggregationUtil.getRangeFilter("availableCount", "system/success/rate", APP_AVAILABLE_LOWWER, APP_AVAILABLE_UPPER, true, true)); // 可用时间
		}
		List<PerformanceView> perfList = new ArrayList<PerformanceView>();
		try {
			perfList = queryPerfService.queryAggregationTermsPerfForApps(RESOURCE_TYPE.APPLICATION, con.add(aggs));
		} catch (ApmDataIndicatorException e) {
			log.error("查询交易详情性能失败", e);
		}
		return perfList;
	}

	@Override
	public List<PerformanceView> queryAppTermsPerfs(Date startDate, Date endDate) {
		SearchCondition con = SearchCondition.newBuilder();
		con.add("timestamp", SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
		
		TermsBuilder aggs = AggregationUtil.getTerms("applicationId", "applicationId");
		aggs.subAggregation(AggregationUtil.getSum("system/throughput/total", "system/throughput/total"));
		aggs.subAggregation(AggregationUtil.getSum("system/response_time/total", "system/response_time/total"));
		aggs.subAggregation(AggregationUtil.getSum("system/error/count", "system/error/count"));
		
		aggs.subAggregation(AggregationUtil.getSum("business/throughput/total", "business/throughput/total"));
		aggs.subAggregation(AggregationUtil.getSum("business/error/count", "business/error/count"));
		aggs.subAggregation(AggregationUtil.getSum("business/response_time/total", "business/response_time/total"));
		List<PerformanceView> perfList = new ArrayList<PerformanceView>();
		
		try {
			perfList = queryPerfService.queryAggregationTermsPerf(null, RESOURCE_TYPE.APPLICATION, con.add(aggs));
		} catch (ApmDataIndicatorException e) {
			log.error("查询交易详情性能失败", e);
		}
		return perfList;
	}

	@Override
	public List<PerformanceView> queryThisDayData(ApplicationSystem application, List<String> appIds, long startDate, long endDate)
			throws ApmDataIndicatorException {
		SearchCondition con = SearchCondition.newBuilder();
		con.add("agentStartTime", SearchConditionComparator.LEFTBETWEEN, startDate, endDate);
		con.add("applicationId", SearchConditionComparator.IN, appIds);
		con.add("tradeTypeName", SearchConditionComparator.EXISTS);
		
		TermsBuilder aggs = AggregationUtil.getTerms("applicationId", "applicationId");
		aggs.subAggregation(AggregationUtil.getCount("business/throughput/total", "_index"));
		aggs.subAggregation(AggregationUtil.getAvg("business/response_time/avg", "elapsed"));
		aggs.subAggregation(AggregationUtil.getFilter("business/error/total", "transactionFlag", new String[]{"1", "3"}));
		aggs.subAggregation(AggregationUtil.getRangeFilter("business/apdex/satisfied", "elapsed", 0, application.getApdex_t(), true, false));
		aggs.subAggregation(AggregationUtil.getRangeFilter("business/apdex/tolerate", "elapsed", application.getApdex_t(), 4 * application.getApdex_t(), true, false));
		
		List<PerformanceView> lastPerf = queryPerfService.queryAggregationTermsPerf(con.add(aggs));
		
		for (PerformanceView perf : lastPerf) {
			perf.put("business/success_rate/avg",
					(perf.get("business/error/total") == null || perf.get("business/throughput/total") == null
							|| Double.parseDouble(perf.get("business/throughput/total").toString()) == 0)
									? "-"
									: new DecimalFormat("0.00").format((Double.parseDouble(perf.get("business/throughput/total").toString())
											- Double.parseDouble(perf.get("business/error/total").toString()))
											/ Double.parseDouble(perf.get("business/throughput/total").toString())
											* 100));
			perf.put("business/apdex/avg", (perf.get("business/apdex/satisfied") == null
					|| perf.get("business/apdex/tolerate") == null) ? "0.0" : (long) (100
					* (Long.parseLong(perf.get("business/apdex/satisfied").toString())
							+ Long.parseLong(perf.get("business/apdex/tolerate").toString()) * 0.5)
					/ Long.parseLong(perf.get("business/throughput/total").toString())));
			perf.remove("business/apdex/satisfied");
			perf.remove("business/apdex/tolerate");
		}
		return lastPerf;
	}

}
